package cn.b.sky.shop.storage.utils;


import java.io.*;
import java.net.Socket;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;

/**
 * @Autor yueding
 */
public final class IOUtil {

    private IOUtil() {

    }

    public static void close(Socket socket) {
        if (socket != null) {
            try {
                socket.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static void close(Closeable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }


    public static void close(AutoCloseable closeable) {
        if (closeable != null) {
            try {
                closeable.close();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }



    public static void destroy(Process process) {
        if (process != null) {
            try {
                process.destroy();
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
    }

    public static void copy(InputStream is, OutputStream os) throws IOException {
        copy(is, os, -1);
    }

    /**
     * 读取的数据如果超过max bytes，则抛出异常
     *
     * @param is
     * @param os
     * @param max
     * @throws IOException
     */
    public static void copy(InputStream is, OutputStream os, long max)
            throws IOException {
        byte[] buffer = new byte[1024];
        int read = 0;
        long readSum = 0;
        while ((read = is.read(buffer)) != -1) {
            os.write(buffer, 0, read);
            if (max > 0) {
                readSum += read;
                if (readSum > max) {
                    throw new IOException("exceed max size :" + max);
                }
            }
        }
    }

    /**
     * 拷贝流is到os，不超过limit字节
     *
     * @param is
     * @param os
     * @param limit
     * @throws IOException
     */
    public static void copyLimit(InputStream is, OutputStream os, long limit)
            throws IOException {
        byte[] buffer = new byte[1024 * 4];
        int read = 0;
        long remain = limit;
        if (limit < 0) {
            throw new IllegalArgumentException("invalid limit " + limit);
        }
        if (limit == 0) {
            return;
        }
        while ((read = is.read(buffer, 0, (int) Math.min(buffer.length, remain))) != -1) {
            os.write(buffer, 0, read);
            remain -= read;
            if (remain == 0) {
                return;
            }
        }
    }

    public static class ProcessCallback {
        private boolean calculateMd5;
        private byte[] md5;

        public ProcessCallback(boolean calculateMd5) {
            this.calculateMd5 = calculateMd5;
        }

        public void onRead(long readTotal) {

        }

        public boolean isCalculateMd5() {
            return calculateMd5;
        }

        public void setCalculateMd5(boolean calculateMd5) {
            this.calculateMd5 = calculateMd5;
        }

        public byte[] getMd5() {
            return md5;
        }

        public void setMd5(byte[] md5) {
            this.md5 = md5;
        }
    }

    public static void copyProcess(InputStream is, OutputStream os, ProcessCallback callback) throws IOException {
        copyProcess(is, os, callback, 0);
    }

    /**
     * 一边拷贝流，一边计算md5，并通知拷贝的进度
     *
     * @param is
     * @param os
     * @param callback
     * @param bytesPerSeconds 每秒限速,<=0时不限制
     * @throws IOException
     */
    public static void copyProcess(InputStream is, OutputStream os, ProcessCallback callback, int bytesPerSeconds) throws IOException {
        if (bytesPerSeconds > 0) {
            is = new RateLimitInputStream(is, bytesPerSeconds);
        }
        byte[] buffer = new byte[8 * 1024];
        int read = 0;
        long readSum = 0;
        MessageDigest alg = null;
        if (callback != null && callback.isCalculateMd5()) {
            try {
                alg = MessageDigest.getInstance("MD5");
            } catch (NoSuchAlgorithmException e) {
                throw new RuntimeException("system do not support MD5 checksum");
            }
        }
        while ((read = is.read(buffer)) > 0) {
            os.write(buffer, 0, read);
            if (alg != null) {
                alg.update(buffer, 0, read);
            }
            readSum += read;
            if (callback != null) {
                callback.onRead(readSum);
            }
        }
        if (alg != null) {
            callback.setMd5(alg.digest());
        }
    }

    public static void copy(InputStream is, byte[] buf, int offset, int length)
            throws IOException {
        if (buf.length < offset + length) {
            throw new ArrayIndexOutOfBoundsException("data size " + (offset + length) + " outof buf size " + buf.length);
        }
        int read = 0;
        while (length > 0 && (read = is.read(buf, offset, length)) != -1) {
            offset += read;
            length -= read;
        }
    }

    public static void copy(File from, File to) throws IOException {
        if (from.equals(to)) {
            return;
        }
        if (!from.exists()) {
            throw new IOException("Source file \"" + from + "\" not exists");
        }
        if (to.isDirectory()) {
            throw new IOException("Target file \"" + to + "\" is a directory");
        }
        if (!to.exists()) {
            File dir = to.getParentFile();
            if (!dir.exists() && !dir.mkdirs() && !dir.isDirectory()) {
                throw new IOException("Unable to create directory  \"" + dir + "\"");
            }
            try {
                to.createNewFile();
            } catch (Exception e) {
                throw new IOException("Unable to create target file  \"" + to + "\"");
            }
        }
        InputStream is = null;
        OutputStream os = null;
        try {
            is = new FileInputStream(from);
            os = new FileOutputStream(to);
            copy(is, os);
        } catch (IOException e) {
            throw e;
        } finally {
            closeQuietly(is);
            closeQuietly(os);
        }
    }

    public static void closeQuietly(Closeable c) {
        try {
            if (c != null)
                c.close();
        } catch (IOException e) {
        }
    }

    public static String getStreamContent(InputStream is, String charset)
            throws IOException {
        return getStreamContent(is, charset, -1);
    }

    public static String getStreamContent(InputStream is, String charset,
                                          int buffsize, long max) throws IOException {
        if (charset == null) {
            charset = "UTF-8";
        }
        if (buffsize <= 0) {
            buffsize = 10240;
        }
        try {
            ByteArrayOutputStream os = new ByteArrayOutputStream(buffsize);
            copy(is, os, max);
            return os.toString(charset);
        } catch (IOException e) {
            throw e;
        }
    }

    public static String getStreamContent(InputStream is, String charset,
                                          int buffsize) throws IOException {
        return getStreamContent(is, charset, buffsize, -1);
    }

    public static String getStreamContent(InputStream is) throws IOException {
        return getStreamContent(is, null, -1);
    }

    public static String getStreamContent(InputStream is, int buffsize)
            throws IOException {
        return getStreamContent(is, null, buffsize);
    }

    public static void copyWriter(BufferedReader input, StringWriter writer)
            throws IOException {
        char[] cbuf = new char[1024];
        int read = 0;
        while ((read = input.read(cbuf)) != -1) {
            writer.write(cbuf, 0, read);
        }
    }

    /**
     * 从输入流in中读取长度为len bytes的数据，并从off位置开始填入数组b，
     *
     * @param in
     * @param b
     * @param off
     * @param len
     * @throws IOException
     */
    public final static void readFully(InputStream in, byte b[], int off, int len) throws IOException {
        if (len < 0)
            throw new IndexOutOfBoundsException();
        if (b.length < off + len) {
            throw new ArrayIndexOutOfBoundsException("buf size is not large enough.");
        }
        int n = 0;
        while (n < len) {
            int count = in.read(b, off + n, len - n);
            if (count < 0)
                throw new EOFException();
            n += count;
        }
    }

    public static void append(File file, InputStream data) throws IOException {
        if (!file.exists()) {
            file.createNewFile();
        }
        FileOutputStream out = new FileOutputStream(file, true);
        try {
            IOUtil.copy(data, out);
        } finally {
            close(out);
        }
    }
}
